package com.gframework.antibrush.limiter;

/**
 * 漏桶算法实现类.
 * <p>
 * 可用于接口防刷上。本类只提供了最为原始的基础操作。
 * </p>
 * 
 * @since 1.0.0
 * @author Ghwolf
 */
public class LeakyBucket {

	/**
	 * 漏桶
	 */
	private final long[] bucket;
	/**
	 * 当前时间减去请求时间超过多少秒就会抛弃
	 */
	private final long millisecond;
	/**
	 * 当前bucket索引
	 */
	private int foot;
	/**
	 * 索引最大值
	 */
	private final int maxFoot;
	/**
	 * 创建时间
	 */
	private long lastActive = System.currentTimeMillis();
	
	/**
	 * 创建一个漏桶.
	 * 每当多少秒内最多请求进入多少个请求。
	 * 
	 * @param millisecond 每当多少毫秒，不能为负数或0
	 * @param maxFlow 最多进入的请求数，不能为负数或0
	 */
	public LeakyBucket(long millisecond, int maxFlow) {
		if (millisecond <= 0 || maxFlow <= 0) {
			throw new IllegalArgumentException(
					"millisecond 和 maxFlow 不能小于等于0，但实际为：millisecond = " + millisecond + " , maxFlow = " + maxFlow);
		}
		this.bucket = new long[maxFlow];
		this.millisecond = millisecond;
		this.maxFoot = maxFlow - 1;
	}

	/**
	 * 判断一个新的请求是否被允许
	 * 
	 * @return 如果被允许，则返回true，否则返回false
	 */
	public boolean requestPassage() {
		this.lastActive = System.currentTimeMillis();
		synchronized (this) {
			if (this.lastActive - bucket[foot] >= millisecond) {
				bucket[foot] = this.lastActive;
				foot = foot == maxFoot ? 0 : foot + 1;
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 此方法可以判断当前桶是否处于空闲状态（既长时间没有新的请求进入）
	 * @return 如果空闲返回true，否则返回false
	 */
	public boolean isFree() {
		return System.currentTimeMillis() - this.lastActive > millisecond;
	}

}
